/**
 * Esta clase representa a los test de la clase ventas.
 */
package ventas;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

import java.util.HashMap;
import java.util.Map;

import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;

import productosYPresentaciones.Producto;
import sucursal.Stock;
import sucursal.Sucursal;
import clientes.Cliente;
import clientes.ListaDeCompras;
import exceptions.VentaYaAbonadaException;
import exceptions.VentasException;

/**
 * @author Lucas Alan Silvestri.
 *
 */
public abstract class VentaTest {

	private Map<Producto, Integer> productosAComprar;
	private ListaDeCompras listaDeComprasDelCliente;
	private Cliente clienteQueRealizaLaCompra;
	private Sucursal sucursalDeLACompra;
	private Producto productoX;
	private Producto productoY;
	private Venta sut;

	/**
	 * @throws java.lang.Exception
	 */
	@Before
	public void setUp() throws Exception {
		this.setListaDeComprasDelCliente(mock(ListaDeCompras.class));
		this.setClienteQueRealizaLaCompra(mock(Cliente.class));
		this.setSucursalDeLACompra(mock(Sucursal.class));
		this.setProductoX(mock(Producto.class));
		this.setProductoY(mock(Producto.class));
		this.setProductosAComprar(new HashMap<Producto, Integer>());
		this.getProductosAComprar().put(this.getProductoX(), 2);
		this.getProductosAComprar().put(this.getProductoY(), 3);
		when(this.getListaDeComprasDelCliente().getProductosAComprar())
				.thenReturn(this.getProductosAComprar());
		when(this.getSucursalDeLACompra().hayStock(this.getProductosAComprar()))
				.thenReturn(true);
		when(this.getProductoX().precioActual()).thenReturn(5F);
		when(this.getProductoY().precioActual()).thenReturn(15F);
	}

	/**
	 * Este test demuestra la capacidad de realizar una compra con exito.
	 * 
	 * @throws VentasException
	 */
	@Test
	public void realizarCompraTest() throws VentasException{
		assertEquals(5, this.getSut().numeroDeProductosAComprar());
		assertEquals(0, this.getSut().numeroDeProductosComprados());
		this.getSut().realizarCompra();
		assertEquals(0, this.getSut().numeroDeProductosAComprar());
		assertEquals(5, this.getSut().numeroDeProductosComprados());
		verify(this.getSucursalDeLACompra()).removerStockDelStockDeSucursal(
				any(Stock.class));
	}

	/**
	 * Este test demuestra la capacidad de no poder realizar una compra con
	 * exito.
	 * 
	 * @throws VentasException
	 */
	@Test(expected = VentasException.class)
	public void noSepudoRealizarCompraTest() throws VentasException{
		when(this.getProductoX().precioActual()).thenReturn(55F);
		when(this.getProductoY().precioActual()).thenReturn(65F);
		this.getSut().realizarCompra();
	}

	/**
	 * este test demuestra que existia una oferta y que se aplico de forma
	 * efectiva al monto que debe pagar el cliente.
	 */
	@Test
	public void montoAPagarConDescuentoTest(){
		when(this.getSucursalDeLACompra().puedeAplicarOferta(
						this.getProductosAComprar())).thenReturn(true);
		when(this.getSucursalDeLACompra().aplicarOfertasConDescuento(
						this.getProductosAComprar())).thenReturn(5F);
		assertEquals(5F, this.getSut().montoAPagarPorLosProductos(), 0.002F);
	}
	
	/**
	 * este test demuetras que no es es una venta con envio
	 * a domicilio.
	 */
	@Test
	public void ventaConEnvioTest() throws VentasException{
		assertEquals(false,this.getSut().soyUnaVentaConEnvio());
	}

	/**
	 * este test demuestra que no existia una oferta y que no se aplico de forma
	 * efectiva al monto que debe pagar el cliente. 
	 */
	@Test
	public void montoAPagarSinDescuentoTest() {
		when(
				this.getSucursalDeLACompra().puedeAplicarOferta(
						this.getProductosAComprar())).thenReturn(false);
		assertEquals(55F, this.getSut().montoAPagarPorLosProductos(), 0.002F);
	}

	/**
	 * este test demuestra que hay stock par realizar la venta.
	 */
	@Test
	public void hayStockTest() {
		assertTrue(this.getSut().hayStock());
	}

	/**
	 * este test demuestra que no hay stock par realizar la venta.
	 */
	@Test
	public void noHayStockTest() {
		when(this.getSucursalDeLACompra().hayStock(this.getProductosAComprar()))
				.thenReturn(false);
		assertFalse(this.getSut().hayStock());
	}

	/**
	 * este test demuestra que el cliente puede pagar los productos para
	 * realizar la compra.
	 * 
	 * @throws VentaYaAbonadaException
	 */
	@Test
	public void elClientePuedePagarTest() throws VentaYaAbonadaException {
		assertTrue(this.getSut().elClientePuedePagarLosProductos());
	}

	/**
	 * este test demuestra que el cliente no puede pagar los productos para
	 * realizar la compra.
	 * 
	 * @throws VentaYaAbonadaException
	 */
	@Test
	public void elClienteNoPuedePagarTest() throws VentaYaAbonadaException {
		when(this.getProductoX().precioActual()).thenReturn(55F);
		when(this.getProductoY().precioActual()).thenReturn(65F);
		assertFalse(this.getSut().elClientePuedePagarLosProductos());
	}

	/**
	 * Este test demuestra que se cumplen los requisitos para poder realizar la
	 * compra.
	 * @throws VentaYaAbonadaException 
	 */
	@Test
	public void seCumplenLosRequisitosParaRealizarLaCompraTest() throws VentaYaAbonadaException {
		when(this.getSucursalDeLACompra().hayStock(this.getProductosAComprar()))
				.thenReturn(true);
		assertTrue(this.getSut().seCumplenLosRequisitosParaRealizarLaCompra());
	}

	/**
	 * Este test demuestra que no se cumplen los requisitos para poder realizar
	 * la compra por falta de stock en la sucursal.
	 * @throws VentaYaAbonadaException 
	 */
	@Test
	public void noSeCumplenLosRequisitosParaRealizarLaCompraNoStockTest() throws VentaYaAbonadaException {
		when(this.getSucursalDeLACompra().hayStock(this.getProductosAComprar()))
				.thenReturn(false);
		assertFalse(this.getSut().seCumplenLosRequisitosParaRealizarLaCompra());
		assertFalse(this.getSut().hayStock());
	}

	/**
	 * Este test demuestra que no se cumplen los requisitos para poder realizar
	 * la compra por que no alcanza la plata que el cliente ofrece para pagar
	 * los productos.
	 * 
	 * @throws VentaYaAbonadaException
	 */
	@Test
	public void noSeCumplenLosRequisitosParaRealizarLaCompraNoAlcanzaTest()
			throws VentaYaAbonadaException {
		when(this.getProductoX().precioActual()).thenReturn(55F);
		when(this.getProductoY().precioActual()).thenReturn(65F);
		assertFalse(this.getSut().seCumplenLosRequisitosParaRealizarLaCompra());
		assertFalse(this.getSut().elClientePuedePagarLosProductos());
	}

	/**
	 * Este test verifica que se haya creado un ticket con el monto apropiado
	 * por la compra.
	 * 
	 * @throws VentasException 
	 */
	@Test
	public void ticketApropiadoTest() throws VentasException{
		this.getSut().realizarCompra();
		assertEquals(55, this.getSut().getTicketDeVenta().getPrecio(), 0.002F);
	}

	/**
	 * Este test verifica que no se haya creado un ticket con el monto apropiado
	 * por la compra.
	 * 
	 * @throws VentasException 
	 */
	@Test
	public void ticketNoApropiadoTest() throws VentasException {
		when(this.getProductoX().precioActual()).thenReturn(5F);
		when(this.getProductoY().precioActual()).thenReturn(6F);
		this.getSut().realizarCompra();
		assertNotEquals(55, this.getSut().getTicketDeVenta().getPrecio(),
				0.002F);
	}

	/**
	 * este test verifica que la fecha de crecion de la venta no es la misma por
	 * la cual se consulta;
	 * 
	 * @throws VentasException
	 */
	@Test
	public void fechaDeCreacionDistintaTest() throws VentasException {
		DateTime fechaAConsultar = new DateTime(2014, 10, 19, 16, 34);
		assertNotEquals(fechaAConsultar, this.getSut()
				.getFechaDeCreacionDeLaVenta());
	}
	
	/**
	 * este test demuestra que no se ha realizado ningun
	 * tipo de compra de productos.
	 */
	@Test
	public void todaviaLaVentaNoSeRealizoTest(){
		assertEquals(false,this.getSut().seRealizoConExito());
		
	}
	
	/**
	 * Este test demuestra que se a podido crear una venta 
	 * con envio pagadado a partir de una venta sin envio realizada.
	 * @throws VentasException
	 */
	@Test
	public void ventaTransformadaTest() throws VentasException{
		assertEquals(false, this.getSut().soyUnaVentaConEnvio());
		this.getSut().realizarCompra();
		assertEquals(true, this.getSut().enviarVentaRealizada().soyUnaVentaConEnvio());	
	}
	
	/**
	 * Este test demuestra que no se a podido crear una venta 
	 * con envio pagadado a partir de una venta por no haber stock.
	 * @throws VentasException
	 */
	@Test(expected = VentasException.class)
	public void ventaNoTransformadaTransformadaPorRealizarseLaCompraTest() throws VentasException{
		this.getSut().enviarVentaRealizada();
	}
	
	/**
	 * este test demuestra que la venta no es una venta
	 * directa sin cliente.
	 */
	@Test
	public void soyUnaVentaDirectaSinClienteTest(){
		assertEquals(false, this.getSut().soyUnaVentaDirectaSinCliente());
	}
	
	/**
	 * @return el sujeto bajo pruebas.
	 */
	protected Venta getSut() {
		return sut;
	}

	/**
	 * @param sut
	 *            define el sujeto bajo pruebas.
	 */
	protected void setSut(Venta sut) {
		this.sut = sut;
	}

	/**
	 * @return la lista de compras del cliente.
	 */
	public ListaDeCompras getListaDeComprasDelCliente() {
		return listaDeComprasDelCliente;
	}

	/**
	 * @param listaDeComprasDelCliente
	 *            es la lista de compras del cliente.
	 */
	protected void setListaDeComprasDelCliente(
			ListaDeCompras listaDeComprasDelCliente) {
		this.listaDeComprasDelCliente = listaDeComprasDelCliente;
	}

	/**
	 * @return el cliente que realiza la compra.
	 */
	public Cliente getClienteQueRealizaLaCompra() {
		return clienteQueRealizaLaCompra;
	}

	/**
	 * @param clienteQueRealizaLaCompra
	 *            es el cliente que realiza la compra.
	 */
	protected void setClienteQueRealizaLaCompra(
			Cliente clienteQueRealizaLaCompra) {
		this.clienteQueRealizaLaCompra = clienteQueRealizaLaCompra;
	}

	/**
	 * @return la sucursal de la compra.
	 */
	public Sucursal getSucursalDeLACompra() {
		return sucursalDeLACompra;
	}

	/**
	 * @param sucursalDeLACompra
	 *            es la sucursal de la compra.
	 */
	protected void setSucursalDeLACompra(Sucursal sucursalDeLACompra) {
		this.sucursalDeLACompra = sucursalDeLACompra;
	}

	/**
	 * @return el producto "X".
	 */
	public Producto getProductoX() {
		return productoX;
	}

	/**
	 * @param productoX
	 *            es el producto "X".
	 */
	protected void setProductoX(Producto productoX) {
		this.productoX = productoX;
	}

	/**
	 * @return el producto "Y".
	 */
	public Producto getProductoY() {
		return productoY;
	}

	/**
	 * @param productoY
	 *            es el producto "Y".
	 */
	protected void setProductoY(Producto productoY) {
		this.productoY = productoY;
	}

	/**
	 * @return the productosAComprar
	 */
	public Map<Producto, Integer> getProductosAComprar() {
		return productosAComprar;
	}

	/**
	 * @param productosAComprar
	 *            the productosAComprar to set
	 */
	protected void setProductosAComprar(Map<Producto, Integer> productosAComprar) {
		this.productosAComprar = productosAComprar;
	}
}
